package com.mofeng.myshop.service.user.impl;

import com.mofeng.myshop.domain.vo.LoginStateVo;
import com.mofeng.myshop.exception.MyException;
import com.mofeng.myshop.until.Constants;
import com.mofeng.myshop.until.MyUtil;
import com.mofeng.myshop.until.ServletUtils;
import com.mofeng.myshop.until.component.CookieUtils;
import com.mofeng.myshop.until.component.RedisUtil;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

@Component
@Schema(description = "管理用户状态的工具类")
@RequiredArgsConstructor
public class SessionServiceImpl {

    private final RedisUtil redisUtil;

    private final CookieUtils cookieUtils;

    private final String TOKEN_KEY = "key_token_";//Token的Key前缀


    private final String KEY_CAPTCHA_CONTENT = "key_captcha_content_";//图灵验证码键名称

    private final String KEY_EMAIL_SEND_IP = "key_email_send_ip_";//邮箱或者手机验证码的IP计数器的Key

    private final String KEY_EMAIL_SEND_ADDRESS = "key_email_send_address_";//用户请求发送邮箱验证码时邮箱地址名称

    private final String LAST_CAPTCHA_KEY = "l_c_i";//图灵验证的key在cookie中存储的值

    private final String COOKIE_TOKEN_KEY = "mofeng_cookie";//cookie中存储的tokenKey

    private final String KEY_EMAIL_CODE_CONTENT = "key_email_code_content_";//邮箱验证码的Key前缀


    private final int SEND_EMAIL_CODE_TIMES = 10;//邮箱发送超过10便会将对方的发邮件功能禁用

    private final Long EMAIL_CODE_SEND_INTERVAL_TIME = 30L;//邮箱验证码发送间隔时间（30秒）

    private final long EMAIL_CODE_EFFECTIVE_TIME = Constants.Times.SECOND * 5L;//邮箱验证码有效时间 (5分钟)


    /**
     * *************************************************token相关*******************************************
     */
    private final int LOGIN_TIMES_WARING = 20;//允许登录验证失败次数，超过则给用户发送邮件提醒并且禁止登陆

    private final String LOGIN_COUNT = "login_count_";//记录登录次数

    private final String SEND_EMAIL_CODE_COUNT = "login_count_";//发送手机或者邮箱验证码的次数

    private final String CAPTCHA_CONTENT_CHECK = "captcha_content_check_";//图灵验证码是否验证过

    /**
     * 清除签到状态
     */
    @Scheduled(cron = "0 0 0 * * ?")
    public void clearSignInState(){

    }

    /**
     * 签到
     * @param userId 用户ID
     * @return 已经签到 放回 false  签到成功 true
     */
    public boolean signIn(Long userId){

        return false;
    }

    /**
     * 检查此IP的登录状态，防止爆破密码
     */
    public int checkLoginCount(LoginStateVo loginState) {
        String ip = loginState.getIp();
        Integer count = (Integer) redisUtil.get(LOGIN_COUNT + ip);
        if (count == null) {
            count = 0;
        }
        loginState.setCount(count);
        if (count >= 10) {
            String cookieTokenKey = getCookieCaptchaKey();
            delCaptchaCheckState(cookieTokenKey);
            delCaptchaContent(cookieTokenKey);
        }
        count++;
        redisUtil.set(LOGIN_COUNT + ip, count, (long) Constants.Times.HOUR);
        if (count >= LOGIN_TIMES_WARING) {
            throw new MyException("您已经尝试登陆 " + count + " 次，系统已禁用您的登陆功能 一小时，请联系管理员或者自行重置密码");
        }
        return count;
    }

    /**
     * 清除登录重试次数
     */
    public void removeLoginCount() {
        String userIp = ServletUtils.getUserIp();
        userIp = userIp.replace(".", "-");
        redisUtil.del(LOGIN_COUNT + userIp);
    }

    /**
     * 检查此IP的登录状态，防止爆破密码
     */
    public int checkSendEmailCodeCount(LoginStateVo sendEmailState) {
        String ip = sendEmailState.getIp();
        Integer count = (Integer) redisUtil.get(SEND_EMAIL_CODE_COUNT + ip);
        if (count == null) {
            count = 0;
        }
        sendEmailState.setCount(count);
        if (count >= LOGIN_TIMES_WARING) {
            throw new MyException("您已经尝试发送邮箱验证码 " + count + " 次，系统已禁用您的邮箱验证码发送功能 一小时");
        }
        if (count > 10) {
            delCaptchaContent(getCookieTokenKey());
            delCaptchaCheckState(getCookieTokenKey());
        }
        count++;
        redisUtil.set(SEND_EMAIL_CODE_COUNT + ip, count, (long) Constants.Times.HOUR);
        return count;
    }

    /**
     * 清除发送邮箱验证码重试次数
     */
    public void removeSendEmailCodeCount() {
        String userIp = ServletUtils.getUserIp();
        userIp = userIp.replace(".", "-");
        redisUtil.del(SEND_EMAIL_CODE_COUNT + userIp);
    }

    /**
     * 设置Toke
     *
     * @param key   token的KEY
     * @param token token
     */
    public void setToken(String key, Object token) {
        redisUtil.set(TOKEN_KEY + key, token, (long) Constants.Times.HOUR);//token有效时间（1小时）
    }

    /**
     * 获取Toke
     *
     * @param key token的KEY
     */
    public <T> T getToken(String key) {
        return (T) redisUtil.get(TOKEN_KEY + key);
    }

    /**
     * 删除Token
     *
     * @param key token的KEY
     */
    public void delToken(String key) {
        redisUtil.del(TOKEN_KEY + key);
    }

    /**
     * 从客户端获取TokenKey
     */
    public <T> T getCookieTokenKey() {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        T result = null;
        result = (T) request.getHeader(COOKIE_TOKEN_KEY);
        if (MyUtil.StringUtil.isEmpty(result)) {
            result = (T) cookieUtils.getCookie(COOKIE_TOKEN_KEY);
        }
        return result;
    }

    /**
     * 设置客户端TokenKey
     */
    public void setCookieTokenKey(String tokenKey) {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletResponse response = requestAttributes.getResponse();
        response.setHeader(COOKIE_TOKEN_KEY, tokenKey);
        cookieUtils.setCookie(COOKIE_TOKEN_KEY, tokenKey);
    }
    /**
     * *************************************************图灵验证码相关*******************************************
     */

    /**
     * 设置图灵验证码
     *
     * @param captchaId 验证码ID
     * @param content   验证码
     */
    public void setCaptchaContent(String captchaId, String content) {
        redisUtil.set(KEY_CAPTCHA_CONTENT + captchaId, content, (long) Constants.Times.SECOND);
    }

    /**
     * 删除图灵验证码
     *
     * @param captchaId 验证码ID
     */
    public void delCaptchaContent(String captchaId) {
        redisUtil.del(KEY_CAPTCHA_CONTENT + captchaId);
    }

    /**
     * 获取图灵验证码
     *
     * @param Key 验证码的KEY
     */
    public <T> T getCaptchaContent(String Key) {
        return (T) redisUtil.get(KEY_CAPTCHA_CONTENT + Key);
    }

    /**
     * 设置图灵验证码验证状态 5分钟内有效
     *
     * @param captchaId 验证码ID
     */
    public void setCaptchaCheckState(String captchaId) {
        redisUtil.set(CAPTCHA_CONTENT_CHECK + captchaId, true, (long) Constants.Times.SECOND * 5);
    }

    /**
     * 删除图灵验证码验证状态
     *
     * @param captchaId 验证码ID
     */
    public void delCaptchaCheckState(String captchaId) {
        redisUtil.del(CAPTCHA_CONTENT_CHECK + captchaId);
    }

    /**
     * 获取图灵验证码 验证状态
     *
     * @param captchaId 验证码的KEY
     */
    public <T> T getCaptchaCheckState(String captchaId) {
        Object o = redisUtil.get(CAPTCHA_CONTENT_CHECK + captchaId);
        return (T) o;
    }

    /**
     * 从客户端获取图灵验证码的Key
     */
    public <T> T getCookieCaptchaKey() {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        T result = null;
        result = (T) request.getHeader(LAST_CAPTCHA_KEY);
        if (MyUtil.StringUtil.isEmpty(result)) {
            result = (T) cookieUtils.getCookie(LAST_CAPTCHA_KEY);
        }
        return result;
    }

    /**
     * 将客户端的图灵验证码Key删除
     */
    public void delCookieCaptchaKey() {
        cookieUtils.deleteCookie(LAST_CAPTCHA_KEY);
    }

    /**
     * 往客户端发送图灵验证码的Key
     */
    public void setCookieCaptchaKey(String codeKey) {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletResponse response = requestAttributes.getResponse();
        response.setHeader(LAST_CAPTCHA_KEY, codeKey);
        cookieUtils.setCookie(LAST_CAPTCHA_KEY, codeKey);
    }

    /**
     * *************************************************邮箱手机验证码相关*******************************************
     */

    /**
     * 获取邮箱/手机验证码
     */
    public <T> T getEmailCodeContent(String emailOrPhone) {
        return (T) redisUtil.get(KEY_EMAIL_CODE_CONTENT + emailOrPhone);
    }

    /**
     * 缓存邮箱/手机验证码
     *
     * @param emailOrPhone 手机号或者邮箱
     * @param code         验证码
     */
    public void setEmailCodeContent(String emailOrPhone, String code) {
        redisUtil.set(KEY_EMAIL_CODE_CONTENT + emailOrPhone, code, EMAIL_CODE_EFFECTIVE_TIME);
    }

    /**
     * 删除邮箱/手机验证码
     *
     * @param emailOrPhone 手机号或者邮箱
     */
    public void delEmailCodeContent(String emailOrPhone) {
        redisUtil.del(KEY_EMAIL_CODE_CONTENT + emailOrPhone);
    }

    /**
     * 设置验证码发送间隔，30秒
     */
    public void addEmailSendIp30(String remoteAddr) {
        redisUtil.set(KEY_EMAIL_SEND_ADDRESS + remoteAddr, "true", (long) 30);
    }

    /**
     * 检查邮箱或者手机验证码得IP 防止暴力请求导致疯狂发送
     */
    public Integer checkEmailSendIp(String remoteAddr) {
        Object code30 = redisUtil.get(KEY_EMAIL_SEND_ADDRESS + remoteAddr);
        if (code30 != null) {
            throw new RuntimeException("30秒内仅能发送一次验证码");
        }
        Integer ipSendTimes = (Integer) redisUtil.get(KEY_EMAIL_SEND_IP + remoteAddr);
        if (ipSendTimes == null) {
            ipSendTimes = 0;
        }
        redisUtil.set(KEY_EMAIL_SEND_IP + remoteAddr, ipSendTimes);
        if (ipSendTimes > SEND_EMAIL_CODE_TIMES) {
            throw new RuntimeException("由于您频繁操作，此功能将会对您停用" + SEND_EMAIL_CODE_TIMES + "钟");
        }
        ipSendTimes++;
        redisUtil.set(KEY_EMAIL_SEND_IP + remoteAddr, ipSendTimes, (long) Constants.Times.SECOND * 10);
        return ipSendTimes;
    }
}
